/*
 * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.pig4cloud.pig.auth.config;

import com.pig4cloud.pig.auth.support.CustomeOAuth2AccessTokenGenerator;
import com.pig4cloud.pig.auth.support.core.CustomeOAuth2TokenCustomizer;
import com.pig4cloud.pig.auth.support.core.FormIdentityLoginConfigurer;
import com.pig4cloud.pig.auth.support.core.PigDaoAuthenticationProvider;
import com.pig4cloud.pig.auth.support.filter.PasswordDecoderFilter;
import com.pig4cloud.pig.auth.support.filter.ValidateCodeFilter;
import com.pig4cloud.pig.auth.support.handler.PigAuthenticationFailureEventHandler;
import com.pig4cloud.pig.auth.support.handler.PigAuthenticationSuccessEventHandler;
import com.pig4cloud.pig.auth.support.password.OAuth2ResourceOwnerPasswordAuthenticationConverter;
import com.pig4cloud.pig.auth.support.password.OAuth2ResourceOwnerPasswordAuthenticationProvider;
import com.pig4cloud.pig.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationConverter;
import com.pig4cloud.pig.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationProvider;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.Arrays;

/**
 * 认证服务器配置类
 * <p>
 * 基于 Spring Authorization Server 的 OAuth2 认证服务器核心配置
 * 主要职责：
 * 1. 配置 OAuth2 授权服务器的安全策略
 * 2. 自定义多种认证方式（密码模式、短信验证码、授权码等）
 * 3. 配置令牌生成策略和存储方式
 * 4. 注册自定义的过滤器和认证处理器
 *
 * @author lengleng
 * @date 2025/05/30
 */
@Configuration
@RequiredArgsConstructor
public class AuthorizationServerConfiguration {

	/**
	 * OAuth2 授权信息服务，用于存储和检索授权信息
	 * 在 PIG 中使用 Redis 实现分布式存储
	 */
	private final OAuth2AuthorizationService authorizationService;

	/**
	 * 密码解密过滤器
	 * 用于解密前端加密传输的密码，增强安全性
	 */
	private final PasswordDecoderFilter passwordDecoderFilter;

	/**
	 * 验证码过滤器
	 * 用于校验图形验证码，防止暴力破解
	 */
	private final ValidateCodeFilter validateCodeFilter;

	/**
	 * OAuth2 授权服务器安全过滤链配置
	 * <p>
	 * 配置说明：
	 * - 仅处理 /oauth2/** 路径下的请求
	 * - 配置各种 OAuth2 端点的个性化处理
	 * - 注册自定义的认证转换器和提供者
	 * - 设置自定义的成功/失败处理器
	 * 
	 * @param http Spring Security 的 HTTP 安全配置构建器
	 * @return {@link SecurityFilterChain} 配置完成的安全过滤链
	 * @throws Exception 配置过程中可能抛出的异常
	 */
	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	public SecurityFilterChain authorizationServer(HttpSecurity http) throws Exception {
		// 配置授权服务器的安全策略，只有 /oauth2/** 的请求才会走如下的配置
		http.securityMatcher("/oauth2/**");
		OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();

		// 增加验证码过滤器，在用户名密码认证之前进行验证码校验
		http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
		// 增加密码解密过滤器，用于解密前端传输的加密密码
		http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class);

		http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化令牌端点配置
			tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证转换器，支持多种认证方式
				.accessTokenResponseHandler(new PigAuthenticationSuccessEventHandler()) // 登录成功处理器，记录日志和发送事件
				.errorResponseHandler(new PigAuthenticationFailureEventHandler());// 登录失败处理器，统一错误响应格式
		}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证配置
		oAuth2ClientAuthenticationConfigurer.errorResponseHandler(new PigAuthenticationFailureEventHandler()))// 处理客户端认证异常
			.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化配置
				.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults())// 自定义授权确认页面
			.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated());// 所有请求都需要认证

		// 配置授权信息存储策略和服务器设置
		http.with(authorizationServerConfigurer.authorizationService(authorizationService)// 使用 Redis 存储授权信息，支持分布式部署
			.authorizationServerSettings(
					AuthorizationServerSettings.builder().issuer(SecurityConstants.PROJECT_LICENSE).build()),// 设置令牌颁发者信息
				Customizer.withDefaults());

		// 配置表单登录，用于授权码模式的用户登录界面
		http.with(new FormIdentityLoginConfigurer(), Customizer.withDefaults());
		DefaultSecurityFilterChain securityFilterChain = http.build();

		// 注册自定义的认证提供者，支持密码模式、短信验证码等多种认证方式
		addCustomOAuth2GrantAuthenticationProvider(http);

		return securityFilterChain;
	}

	/**
	 * 自定义令牌生成器配置
	 * <p>
	 * 生成规则：
	 * - Access Token 格式：client:username:uuid
	 * - 支持在令牌中添加自定义声明（用户信息、权限等）
	 * - 同时生成访问令牌和刷新令牌
	 * 
	 * @return OAuth2TokenGenerator 配置好的令牌生成器
	 */
	@Bean
	public OAuth2TokenGenerator oAuth2TokenGenerator() {
		CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator();
		// 注入令牌自定义器，用于在令牌中添加用户详细信息和权限信息
		accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer());
		// 使用委托模式，同时支持访问令牌和刷新令牌的生成
		return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator());
	}

	/**
	 * 认证请求转换器配置
	 * <p>
	 * 将不同类型的认证请求转换为对应的认证令牌：
	 * - 密码模式：用户名密码 -> OAuth2ResourceOwnerPasswordAuthenticationToken
	 * - 短信模式：手机号验证码 -> OAuth2ResourceOwnerSmsAuthenticationToken
	 * - 刷新令牌：refresh_token -> OAuth2RefreshTokenAuthenticationToken
	 * - 客户端模式：client_credentials -> OAuth2ClientCredentialsAuthenticationToken
	 * - 授权码模式：authorization_code -> OAuth2AuthorizationCodeAuthenticationToken
	 * 
	 * @return DelegatingAuthenticationConverter 支持多种认证方式的转换器
	 */
	@Bean
	public AuthenticationConverter accessTokenRequestConverter() {
		return new DelegatingAuthenticationConverter(Arrays.asList(
				new OAuth2ResourceOwnerPasswordAuthenticationConverter(),
				new OAuth2ResourceOwnerSmsAuthenticationConverter(), new OAuth2RefreshTokenAuthenticationConverter(),
				new OAuth2ClientCredentialsAuthenticationConverter(),
				new OAuth2AuthorizationCodeAuthenticationConverter(),
				new OAuth2AuthorizationCodeRequestAuthenticationConverter()));
	}

	/**
	 * 注册自定义的认证提供者
	 * <p>
	 * 向 Spring Security 注册自定义的认证处理器：
	 * 1. PigDaoAuthenticationProvider - 处理用户名密码的 DAO 认证
	 * 2. OAuth2ResourceOwnerPasswordAuthenticationProvider - 处理 OAuth2 密码模式
	 * 3. OAuth2ResourceOwnerSmsAuthenticationProvider - 处理短信验证码登录
	 * 
	 * @param http HTTP 安全配置对象，用于获取认证管理器和授权服务
	 */
	private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) {
		AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
		OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);

		// 创建密码模式认证提供者，支持用户名密码登录
		OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
				authenticationManager, authorizationService, oAuth2TokenGenerator());

		// 创建短信验证码认证提供者，支持手机号验证码登录
		OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
				authenticationManager, authorizationService, oAuth2TokenGenerator());

		// 处理传统的用户名密码认证令牌（用于表单登录）
		http.authenticationProvider(new PigDaoAuthenticationProvider());
		// 处理 OAuth2 密码模式的认证令牌
		http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
		// 处理 OAuth2 短信验证码模式的认证令牌
		http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
	}

}
